package cn.okcode.slowjson.parser;

import cn.okcode.slowjson.Config;
import cn.okcode.slowjson.JsonException;
import cn.okcode.slowjson.ast.*;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 递归下降分析
 *
 * @author pyk13
 */
public final class RecursiveDescentParserImpl implements JsonParser {
    private Token currToken;
    private final JsonLexer lexer;

    private final Config config;

    private final List<Visitor> visitors = new ArrayList<>();

    public RecursiveDescentParserImpl(String source, Config config) {
        this.config = config;
        this.lexer = new JsonLexer(source, config);
    }

    private void nextToken() {
        lexer.nextToken();
        this.currToken = lexer.getToken();
    }

    private Token peekToken() {
        return lexer.peekToken();
    }

    @Override
    public final boolean isObject() {
        Token token = peekToken();
        return token.getToken() == TokenKind.OBJ_LEFT;
    }

    @Override
    public final boolean isArray() {
        Token token = peekToken();
        return token.getToken() == TokenKind.ARR_LEFT;
    }

    @Override
    public void addVisitor(Visitor visitor) {
        this.visitors.add(visitor);
    }

    public AbstractNode<?> parse() {
        nextToken();
        AbstractNode<?> node = doParse(null);
        visitors.forEach(node::accept);
        return node;
    }

    private AbstractNode<?> doParse(NodeContext context) {
        AbstractNode<?> node = null;
        NodeContext childContext = new NodeContext(context);
        switch (currToken.getToken()) {
            case OBJ_LEFT:
                //对象
                node = doParseObject(childContext);
                break;
            case ARR_LEFT:
                //数组
                node = doParseArray(childContext);
                break;
            case TRUE:
            case FALSE:
                node = doParseBool();
                break;
            case NULL:
                node = doParseNull();
                break;
            case NUMBER:
                node = doParseNumber();
                break;
            case STRING:
                node = doParseString();
                break;
            case EOF:
                // 解析结束
                break;
            case IDENTIFIER:
                // 标识符不应该出现在这里，只允许在匹配对象属性的时候出现
            default:
                throw new JsonException(lexer.formatMessage("无法识别的字符"));
        }
        if (node != null) {
            node.setContext(childContext);
        }
        return node;
    }


    private BoolNode doParseBool() {
        BoolNode boolNode = new BoolNode(currToken.getBooleanValue());
        nextToken();
        return boolNode;
    }

    private NumberNode doParseNumber() {
        NumberNode numberNode = new NumberNode(currToken.getNumberValue());
        nextToken();
        return numberNode;
    }

    private NullNode doParseNull() {
        NullNode nullNode = new NullNode();
        nextToken();
        return nullNode;
    }

    private StringNode doParseString() {
        StringNode stringNode = new StringNode(currToken.getStringValue());
        nextToken();
        return stringNode;
    }

    private PropertyNode doParseProperty() {
        // 校验Token类型
        boolean nameStyle = true;
        if (currToken.getToken() != TokenKind.STRING) {
            if (config.isLoosePropertyMatching()) {
                if (currToken.getToken() != TokenKind.IDENTIFIER) {
                    throw new JsonException(lexer.formatMessage("应该出现属性名"));
                }
                nameStyle = false;
            }
        }

        String propertyName = currToken.getStringValue();
        if (propertyName == null || propertyName.isEmpty()) {
            throw new JsonException(lexer.formatMessage("属性名必须是非空字符串"));
        }
        PropertyNode propertyNode = new PropertyNode(propertyName, nameStyle);
        //属性后面是冒号:
        nextToken();
        if (currToken.getToken() != TokenKind.COLON) {
            throw new JsonException(lexer.formatMessage("错误的字符,应该出现冒号':'"));
        }
        nextToken();
        return propertyNode;
    }

    /**
     * 解析对象
     * {
     * "name1":"string value1",
     * "name2":"string value2",
     * "name3":{
     * "name1":"string value1",
     * "name2":true,
     * "name2":null,
     * },
     * "name4":123.3,
     * }
     *
     * @return 对象节点
     */
    private ObjectNode doParseObject(NodeContext context) {
        nextToken();
        Map<PropertyNode, AbstractNode<?>> properties = new LinkedHashMap<>();
        while (currToken.getToken() != TokenKind.OBJ_RIGHT) {

            PropertyNode propertyNode = doParseProperty();
            properties.put(propertyNode, doParse(context));

            // 对象元素后面必须是分隔符逗号','或者右括号结束]
            if (currToken.getToken() == TokenKind.COMMA) {
                nextToken();
            } else if (currToken.getToken() != TokenKind.OBJ_RIGHT) {
                throw new JsonException(lexer.formatMessage("正在匹配对象,应该出现右大括号'}'或者逗号','"));
            }
        }
        ObjectNode arrayNode = new ObjectNode(properties);
        nextToken();
        return arrayNode;
    }

    /**
     * 解析数组
     * 数组元素可能是对象、数字、bool、字符串
     * []
     * [1,2,{},"aaa",[1,2,3],[],true,false,null,]
     * 只允许最后出现多余的','
     *
     * @return 数组节点
     */
    private ArrayNode doParseArray(NodeContext context) {
        nextToken();
        List<AbstractNode<?>> childNodes = new ArrayList<>();

        while (currToken.getToken() != TokenKind.ARR_RIGHT) {

            childNodes.add(doParse(context));

            // 数组元素后面必须是分隔符逗号','或者右括号结束]
            if (currToken.getToken() == TokenKind.COMMA) {
                nextToken();
            } else if (currToken.getToken() != TokenKind.ARR_RIGHT) {
                throw new JsonException(lexer.formatMessage("正在匹配数组元素,应该出现逗号','或者右中括号']'"));
            }
        }

        ArrayNode arrayNode = new ArrayNode(childNodes);
        nextToken();
        return arrayNode;
    }
}
